﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sahit.iSoilHotel.Application.Seedwork;
using Sahit.iSoilHotel.Application.MainBoundedContext.Tavern.Interfaces;
using Sahit.iSoilHotel.Application.MainBoundedContext.DTO.RecordAgg;
using Sahit.iSoilHotel.Domain.MainBoundedContext.Tavern.Aggregates.RecordAgg;
using Sahit.iSoilHotel.Domain.MainBoundedContext.Tavern.Services;
using Sahit.iSoilHotel.Domain.MainBoundedContext.Tavern.Aggregates.CustomerAgg;
using Sahit.iSoilHotel.Domain.Seedwork;
using Sahit.iSoilHotel.Application.MainBoundedContext.DTO.FilterAgg;
using Sahit.iSoilHotel.Domain.Seedwork.Specification;

namespace Sahit.iSoilHotel.Application.MainBoundedContext.Tavern.Services
{
    public class RecordService:IRecordService
    {
        #region Members

        private readonly IRecordRepository recordRepository;
        private readonly IReserveService reserveService;
        private readonly ICheckInService checkinService;
        private readonly ICheckOutService checkoutService;
        private readonly ICustomerService customerService;
        private readonly IPriceCodeService priceCodeService;
        private readonly IRoomService roomService;

        #endregion

        #region Constructor

        public RecordService(
            IRecordRepository recordRepository
            ,ICustomerService customerService
            , IPriceCodeService priceCodeService
            , IRoomService roomService
            , ICheckInService checkInService
            , ICheckOutService checkOutService
            ,IReserveService reserveService)
        {
            if (recordRepository == null)
                throw new ArgumentNullException("RecordRepository", "RecordRepository is null!");

            this.recordRepository = recordRepository;
            this.customerService = customerService;
            this.priceCodeService = priceCodeService;
            this.roomService = roomService;
            this.reserveService = reserveService;
            this.checkinService = checkInService;
            this.checkoutService = checkOutService;
        }
        #endregion

        public void Reserve(RecordDTO record)
        {
            var recordEntity = record.ProjectedAs<Record>();

            recordEntity.GenerateNewIdentity();
            
            var mainCustomer = recordEntity.Customers.FirstOrDefault();
            recordEntity.Customers = new List<Customer>();
            recordEntity.Customers.Add(AddOrUpdateNewCustomer(mainCustomer));

            recordEntity.PriceCode = priceCodeService.GetPriceCode(recordEntity.PriceCode.ID);
            recordEntity.Room = roomService.GetRoom(recordEntity.Room.ID);

            reserveService.Reserve(recordEntity);

            try
            {
                recordRepository.Add(recordEntity);
                recordRepository.UnitOfWork.Commit();
            }
            catch (Exception)
            {
                recordRepository.UnitOfWork.RollbackChanges();
            }
        }

        protected bool CheckIsAnExistCustomer(Customer customer)
        {
            return customerService.CheckIsOldCustomer(customer.IDCard);
        }

        protected Customer AddOrUpdateNewCustomer(Customer customer)
        {
            if (!CheckIsAnExistCustomer(customer))
            {
                return customerService.AddNewCustomer(customer);
            }
            return customerService.GetCustomer(customer.IDCard);
        }

        public List<RecordDTO> GetRecords(int? statusID)
        {
            if (statusID == null)
            {
                List<Record> allrecords = recordRepository.GetAll().ToList();
                return allrecords.ProjectedAsCollection<RecordDTO>();
            }
            return recordRepository.GetFiltered(r => r.StatusID == statusID).ProjectedAsCollection<RecordDTO>();
        }

        public List<RecordDTO> GetRecords(RecordFilterDTO filter)
        {
            Specification<Record> filterRecords = RecordSpecifications.RecordSearch(filter.RecordID, filter.RecordStatusIDs, filter.RoomTypeID, filter.RoomNumber, filter.RoomID);
            List<Record> rooms = recordRepository.AllMatching(filterRecords).ToList();
            return rooms.ProjectedAsCollection<RecordDTO>();
        }


        public void CheckIn(RecordDTO record)
        {
            var recordEntity = record.ProjectedAs<Record>();

            var mainCustomer = recordEntity.Customers.FirstOrDefault();
            recordEntity.Customers = new List<Customer>();
            recordEntity.Customers.Add(AddOrUpdateNewCustomer(mainCustomer));

            checkinService.CheckIn(recordEntity);

            try
            {
                if(record.ID == Guid.Empty)
                    recordRepository.Add(recordEntity);
                else
                recordRepository.Modify(recordEntity);
                recordRepository.UnitOfWork.Commit();

                roomService.SetRoomStatus(recordEntity.RoomID, recordEntity.Room.Status);
            }
            catch (Exception)
            {
                recordRepository.UnitOfWork.RollbackChanges();
            }
        }

        public void CheckOut(RecordDTO record)
        {
            var recordEntity = record.ProjectedAs<Record>();

            var mainCustomer = recordEntity.Customers.FirstOrDefault();
            recordEntity.Customers = new List<Customer>();
            recordEntity.Customers.Add(AddOrUpdateNewCustomer(mainCustomer));

            recordEntity.PriceCode = priceCodeService.GetPriceCode(recordEntity.PriceCode.ID);
            recordEntity.Room = roomService.GetRoom(recordEntity.Room.ID);

            checkoutService.CheckOut(recordEntity);

            try
            {
                recordRepository.Modify(recordEntity);
                recordRepository.UnitOfWork.Commit();
            }
            catch (Exception)
            {
                recordRepository.UnitOfWork.RollbackChanges();
            }
        }
    }
}
